#include <mutex>
#include <condition_variable>
#include <queue>

// thread-safe MPMC unbouned blocking queue

template <typename T>
class thread_safe_queue {
public:
    thread_safe_queue()
    {}

    thread_safe_queue(const thread_safe_queue& that) = delete;

    void put(T item) {
        std::lock_guard<std::mutex> lock(mtx_);
        queue_.push(item);
        not_empty_cond_.notify_one();
    }

    bool try_take(T& head_item) {
        std::lock_guard<std::mutex> lock(mtx_);
        if (!queue_.empty()) {
            head_item = std::move(queue_.front());
            queue_.pop();
            return true;
        } else {
            return false;
        }
    }

    void take(T& head_item) {
        std::unique_lock<std::mutex> lock(mtx_);

        not_empty_cond_.wait(
            lock,
            [this]() { return !queue_.empty(); }
        );

        head_item = std::move(queue_.front());
        queue_.pop();
    }

private:
    std::queue<T> queue_;
    std::mutex mtx_;
    std::condition_variable not_empty_cond_;
};
